package com.haida.translation_web.aop.aspect;

import com.haida.translation_web.aop.ann.LimitAccess;
import com.haida.translation_web.pojo.respone.R;
import com.haida.translation_web.util.CommontMethod;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @Author LHJ_Jack
 * @Description TODO
 * @Date 2021/05/27 星期四 14:41
 */
@Aspect
@Component
@Log4j2
@Order
public class LimitAccessAspect {


    @Getter
    private Map<String, List<Long>> limitMap = new HashMap<>();

    @Pointcut("@annotation(limitAccess)")
    public void limitAccessPointCut(LimitAccess limitAccess) {
        // 限制接口调用切面类
    }

    @Around(value = "limitAccessPointCut(limitAccess)", argNames = "point,limitAccess")
    public Object doAround(ProceedingJoinPoint point, LimitAccess limitAccess) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (null != attributes) {
            String className = point.getTarget().getClass().getName();
            String methodName = point.getSignature().getName();
            HttpServletRequest request = attributes.getRequest();
            String key = className + "." + methodName + "#" + request.getSession().getId();
            List<Long> millisecondList = limitMap.get(key);
            long now = System.currentTimeMillis();
            if (null == millisecondList) {
                List<Long> list = new ArrayList<>();
                list.add(now);
                limitMap.put(key, list);
            } else {
                List<Long> newMillisecondList = new ArrayList<>(millisecondList.size());
                millisecondList.forEach(millisecond -> {
                    // 当前访问时间 - 历史访问时间 < 限制时间
                    if (now - millisecond < limitAccess.millisecond()) newMillisecondList.add(millisecond);
                });
                // 时间段内超过访问频次上限 - 阻断
                if (newMillisecondList.size() >= limitAccess.frequency()) {
                    log.info("接口调用过于频繁 {}", key);

                    String ip = CommontMethod.getIpAddr(request);//获取ip地址
                    System.out.println(ip);
                    // return ResponseResult.ok(Boolean.FALSE).code(Const.ReturnCode.REQUEST_TOO_BUSY).message(Const.ReturnMessage.REQUEST_TOO_BUSY).build();
                    return R.error(2000,"请休息一会再来");
                }
                newMillisecondList.add(now);
                // 更新接口访问记录
                limitMap.put(key, newMillisecondList);
            }
        }
        return point.proceed();
    }

    /**
     * @Description 过滤受限的IP，剔除已经到期的限制IP
     * @param limitedIpMap
     */
    private void filterLimitedIpMap(Map<String, Long> limitedIpMap) {
        if (limitedIpMap == null) {
            return;
        }
        Set<String> keys = limitedIpMap.keySet();
        Iterator<String> keyIt = keys.iterator();
        long currentTimeMillis = System.currentTimeMillis();
        while (keyIt.hasNext()) {
            long expireTimeMillis = limitedIpMap.get(keyIt.next());
            if (expireTimeMillis <= currentTimeMillis) {
                keyIt.remove();
            }
        }
    }

    /**
     * @Description 是否是被限制的IP
     * @param limitedIpMap
     * @param ip
     * @return true : 被限制 | false : 正常
     */
    private boolean isLimitedIP(Map<String, Long> limitedIpMap, String ip) {
        if (limitedIpMap == null || ip == null) {
            // 没有被限制
            return false;
        }
        Set<String> keys = limitedIpMap.keySet();
        Iterator<String> keyIt = keys.iterator();
        while (keyIt.hasNext()) {
            String key = keyIt.next();
            if (key.equals(ip)) {
                // 被限制的IP
                return true;
            }
        }
        return false;
    }

    /**
     * 初始化用户访问次数和访问时间
     *
     * @param ipMap
     * @param ip
     */
    private void initIpVisitsNumber(Map<String, Long[]> ipMap, String ip) {
        Long[] ipInfo = new Long[2];
        ipInfo[0] = 0L;// 访问次数
        ipInfo[1] = System.currentTimeMillis();// 初次访问时间
        ipMap.put(ip, ipInfo);
    }

}
